Skip to content

fix(cli): bind transparent agent runs to gateway credentials#449

Merged
rapids-bot[bot] merged 4 commits into
NVIDIA:mainfrom
bbednarski9:fix/hermes-transparent-upstream-auth
Jul 16, 2026
Merged

fix(cli): bind transparent agent runs to gateway credentials#449
rapids-bot[bot] merged 4 commits into
NVIDIA:mainfrom
bbednarski9:fix/hermes-transparent-upstream-auth

Conversation

@bbednarski9

@bbednarski9 bbednarski9 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Overview

Bind every transparent nemo-relay run --agent ... invocation to a fresh gateway credential, and make provider-authentication ownership explicit when plugins select a different upstream.

The original failure surfaced through Hermes, but interpreting Hermes's Bearer no-key-required literal inside the generic gateway would have coupled Relay to one harness implementation and left cross-provider credential ownership ambiguous. This PR instead establishes one provider- and harness-neutral boundary:

  1. Relay authenticates its launched child at transparent-gateway ingress.
  2. Relay consumes that invocation credential before interceptors execute.
  3. The source route owns source credentials until a plugin explicitly selects a target.
  4. An explicit target binding owns target authentication; source and ambient credentials cannot leak across that boundary.

No literal Bearer no-key-required behavior remains in Relay.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Design

Invocation-bound gateway authentication
  • Generate a new 256-bit random proxy credential for every transparent invocation.
  • Deliver it only to the launched child through NEMO_RELAY_PROXY_CREDENTIAL and the harness's supported configuration mechanism.
  • Validate it in constant time at gateway ingress, before request decoding, interceptors, routing, or upstream dispatch.
  • Return HTTP 401 for a missing or foreign credential.
  • Consume it at ingress so it cannot reach providers or observable request headers.
  • Redact secret-valued child environment variables from --print output.

Hermes, Codex, and Claude Code share this gateway contract while retaining their native configuration surfaces. A harness that supports a dedicated header may keep a separate provider credential alongside the proxy credential.

Explicit provider-auth ownership

The gateway records typed source-credential provenance before routing and resolves credentials based on the final destination:

Dispatch outcome Authentication behavior
Original route Preserve legitimate source credentials and existing upstream-key behavior.
Explicit authenticated target Remove source credentials, disable ambient-key injection, then apply Relay-owned target-binding headers.
Explicit keyless target Remove source credentials and disable ambient-key injection; dispatch without auth.
Cross-protocol target Strip all recognized source-provider carriers before applying the selected target's protocol-specific binding.
Foreground gateway Preserve existing behavior; no transparent-invocation credential or placeholder interpretation.

This prevents a source Authorization, x-api-key, api-key, or anthropic-api-key value from accidentally becoming the credential for a plugin-selected destination.

Detailed design walkthroughs

Compatibility

This is not a public API breaking change. It intentionally tightens only transparent agent gateways: provider routes accept requests from the child process launched for that invocation. Foreground gateways and managed-sidecar proof behavior remain unchanged.

Validation

Local validation completed:

  • cargo check -p nemo-relay-cli --all-targets
  • cargo fmt --all -- --check
  • cargo clippy -p nemo-relay-cli --all-targets -- -D warnings
  • cargo test -p nemo-relay-cli
    • 1,047 unit tests passed
    • 10 architecture tests passed
    • 81 CLI integration tests passed
  • cargo test -p nemo-relay-switchyard
    • 34 unit tests and 4 contract tests passed
  • cargo build -p nemo-relay-cli --features switchyard

Focused regressions cover:

  • case-insensitive replacement of stale Claude proxy headers;
  • high-entropy generation and constant-time validation;
  • missing and foreign invocation credentials;
  • dedicated-header and standard OpenAI/Anthropic credential carriers;
  • source credential preservation on the original route;
  • source and ambient credential removal for keyless and cross-protocol targets;
  • binding-owned target authentication;
  • foreground and managed-sidecar compatibility;
  • Codex, Claude Code, and Hermes launch configuration; and
  • proxy omission from upstream forwarding and observability.

Real-service validation completed against an authenticated OpenAI-compatible endpoint:

  • A macOS transparent Hermes smoke returned the exact expected response.
  • A Linux aarch64 Fabric/Harbor Terminal-Bench regex-log run through nemo-relay run --agent hermes completed with reward 1.0.
  • The Harbor run produced one coherent trace with 23 OpenInference spans, including 7 LLM spans with model and token usage, plus 1,678 ATOF events and 23 ATIF steps.
  • Dynamic plugin registration, enablement, and validation succeeded.
  • Artifact scans found no generated proxy credential, legacy placeholder, HTTP 401/403 response, or gateway-authentication error.

Where should the reviewer start?

Start with crates/cli/src/provider_auth.rs, which defines generation, comparison, supported carriers, and typed credential provenance. Then review:

  1. crates/cli/src/process/prepared.rs and the agent launchers for per-run delivery;
  2. crates/cli/src/server/mod.rs for ingress enforcement; and
  3. crates/cli/src/gateway/mod.rs for source-versus-target authentication ownership.

The HTTP-level regressions in crates/cli/tests/coverage/shared/server_tests.rs and dispatch-policy regressions in crates/cli/tests/coverage/shared/gateway_tests.rs describe the intended behavior end to end.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

Summary by CodeRabbit

  • New Features
    • Added secure transparent-proxy authentication for gateway requests, with structured authorization decisions and explicit 401 UNAUTHORIZED responses when authorization fails.
    • Automatically propagates transparent-proxy credentials into Claude, Codex, and Hermes configurations (HTTP header settings and Hermes API key templating).
    • Redacts transparent-proxy secrets in prepared run plans and prevents proxy tokens from being forwarded or recorded in observability metadata.
  • Bug Fixes
    • Preserves existing custom provider headers while appending the transparent-proxy header/value; replaces any prior transparent-proxy entry case-insensitively so only one remains.

@copy-pr-bot

copy-pr-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 2336902f-c305-44bc-a771-dd2df8361b78

📥 Commits

Reviewing files that changed from the base of the PR and between d2e5e1d and 5f3780d.

📒 Files selected for processing (1)
  • crates/cli/tests/coverage/shared/gateway_tests.rs
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/shared/gateway_tests.rs
🔇 Additional comments (1)
crates/cli/tests/coverage/shared/gateway_tests.rs (1)

12-12: LGTM!

Also applies to: 1133-1147


Walkthrough

Transparent runs generate invocation-bound proxy credentials, deliver them through Claude, Codex, and Hermes configuration, consume them at gateway ingress, and propagate structured authorization through routing and upstream forwarding. Tests cover credential handling, redaction, dispatch policies, and HTTP 401 rejection.

Changes

Transparent proxy authentication

Layer / File(s) Summary
Credential contracts and verification
crates/cli/src/provider_auth.rs, crates/cli/src/error.rs, crates/cli/src/lib.rs
Adds generated proxy credentials, constant-time verification, provider credential classification, structured authorization, and unauthorized responses.
Agent credential delivery and secret handling
crates/cli/src/process/*, crates/cli/src/agents/*, crates/cli/tests/coverage/agents/*
Stores and propagates proxy credentials through launch preparation, configures Claude, Codex, and Hermes, and redacts registered secret environment values.
Transparent gateway ingress authorization
crates/cli/src/server/mod.rs, crates/cli/tests/coverage/shared/{server_tests.rs,installer_tests.rs,mcp_tests.rs}
Threads credentials through transparent listener startup, consumes invocation headers before forwarding, and validates missing, foreign, and valid credentials.
Credential-aware gateway forwarding
crates/cli/src/gateway/*, crates/cli/tests/coverage/shared/gateway_tests.rs
Replaces boolean authorization plumbing with structured source/target credential policies and prevents source or proxy credential leakage across explicit dispatch routes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant TransparentGateway
  participant TargetBinding
  participant Provider
  Agent->>TransparentGateway: Send invocation and provider credentials
  TransparentGateway->>TransparentGateway: Consume invocation credential
  TransparentGateway->>TargetBinding: Resolve source or explicit target route
  TargetBinding->>Provider: Forward sanitized request with selected auth
  Provider-->>TransparentGateway: Return upstream response
  TransparentGateway-->>Agent: Return response
Loading

Possibly related PRs

  • NVIDIA/NeMo-Relay#440: Modifies the provider-auth injection and gateway request forwarding paths touched by this change.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the transparent-agent gateway credential binding changes.
Description check ✅ Passed The description matches the template with Overview, Details/Design, reviewer guidance, related issue, compatibility, and validation sections.
Linked Issues check ✅ Passed The changes implement invocation-bound proxy auth, explicit source/target credential ownership, and the required test coverage for #448.
Out of Scope Changes check ✅ Passed The diff stays focused on transparent-agent auth, gateway routing, and related tests; no unrelated feature work is evident.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size:M PR is medium Bug issue describes bug; PR fixes bug lang:rust PR changes/introduces Rust code labels Jul 16, 2026
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
@bbednarski9
bbednarski9 force-pushed the fix/hermes-transparent-upstream-auth branch from 5074b8f to 235ef2d Compare July 16, 2026 05:40
@github-actions github-actions Bot added size:L PR is large and removed size:M PR is medium labels Jul 16, 2026
@bbednarski9 bbednarski9 changed the title fix(cli): replace Hermes loopback auth before upstream forwarding fix(cli): bind transparent agent runs to gateway credentials Jul 16, 2026
@willkill07 willkill07 added this to the 0.6 milestone Jul 16, 2026
@bbednarski9

Copy link
Copy Markdown
Contributor Author

Design walkthrough 1/3: invocation credential lifecycle

The proxy credential authenticates the child process to the exact transient gateway invocation that launched it. It is not a provider credential and is consumed before Relay runs any request or routing logic.

sequenceDiagram
    autonumber
    participant CLI as nemo-relay run
    participant Launch as PreparedAgentLaunch
    participant Agent as Hermes / Codex / Claude Code
    participant Ingress as Transparent gateway ingress
    participant Pipeline as Relay intercept and dispatch pipeline

    CLI->>Launch: Prepare one transparent agent invocation
    Launch->>Launch: Generate random 256-bit proxy credential
    Launch->>Agent: Child-only environment and harness configuration
    Launch->>Ingress: Store expected credential for this gateway instance
    Agent->>Ingress: Provider request plus proxy credential
    Ingress->>Ingress: Extract supported credential carrier
    Ingress->>Ingress: Compare in constant time

    alt Credential missing or foreign
        Ingress-->>Agent: HTTP 401 Unauthorized
        Note over Ingress,Pipeline: Body decoding, interceptors, routing, and upstream dispatch never run
    else Credential matches this invocation
        Ingress->>Ingress: Consume proxy credential
        Ingress->>Pipeline: Request plus typed source-credential provenance
        Pipeline-->>Agent: Normal provider response
    end

    CLI->>Agent: Redact secret environment values from --print output
Loading

Security properties:

  • Every transparent invocation receives a different high-entropy credential.
  • The credential is shared only with the launched child and its loopback gateway.
  • A credential from another concurrent or previous run is rejected.
  • The credential cannot reach the upstream request or observable-header capture.
  • Foreground gateways remain unchanged; they do not reinterpret a well-known Hermes literal.

@bbednarski9

Copy link
Copy Markdown
Contributor Author

Design walkthrough 2/3: one gateway contract, harness-specific delivery

Relay owns one authentication contract while each harness uses its supported configuration surface. This avoids hard-coding a private placeholder from any agent implementation into the gateway.

flowchart LR
    R["Per-run proxy credential"] --> E["Child-only NEMO_RELAY_PROXY_CREDENTIAL"]

    E --> HCFG["Hermes isolated model overlay"]
    E --> CCFG["Codex env_http_headers"]
    E --> ACFG["Claude Code ANTHROPIC_CUSTOM_HEADERS"]

    subgraph Hermes["Hermes transport"]
        HCFG --> HAUTH["Standard custom-provider API-key carrier"]
    end

    subgraph Codex["Codex transport"]
        CCFG --> CHDR["x-nemo-relay-proxy-token"]
        CPROV["Independent provider Authorization, if present"] --> CREQ["Codex request"]
        CHDR --> CREQ
    end

    subgraph Claude["Claude Code transport"]
        ACFG --> AHDR["x-nemo-relay-proxy-token"]
        ACUSTOM["Existing custom headers"] --> AREQ["Claude request"]
        AHDR --> AREQ
    end

    HAUTH --> G["Shared transparent-gateway validator"]
    CREQ --> G
    AREQ --> G
    G --> N["Normalized authenticated source request"]
Loading
Harness Delivery mechanism Provider credential behavior
Hermes Isolated custom-provider api_key reference The proxy credential occupies Hermes's loopback auth carrier and is consumed by Relay.
Codex Dedicated header via env_http_headers An independent provider Authorization value can remain available as source provenance.
Claude Code Dedicated header via ANTHROPIC_CUSTOM_HEADERS Existing custom headers are preserved; Anthropic provider-key carriers are handled independently.

New harnesses only need to deliver the per-run value using a supported child configuration mechanism. They do not require new credential literals or harness-specific branches in gateway authentication.

@bbednarski9

Copy link
Copy Markdown
Contributor Author

Design walkthrough 3/3: provider authentication ownership after routing

Invocation authentication ends at ingress. Provider authentication is then resolved according to who owns the final destination.

flowchart TD
    A["Authenticated source request"] --> B["Record source credential disposition"]
    B --> C["Run request interceptors"]
    C --> D{"Explicit target dispatch?"}

    D -- "No" --> S1["Keep source route"]
    S1 --> S2["Preserve legitimate source credential"]
    S2 --> S3["Apply existing ambient provider-key behavior when needed"]
    S3 --> U1["Dispatch to original upstream"]

    D -- "Yes" --> T1["Consume Relay internal route and URL headers"]
    T1 --> T2["Remove all source provider credential carriers"]
    T2 --> T3["Disable ambient environment-key injection"]
    T3 --> T4["Apply Relay-owned target-binding headers"]
    T4 --> E{"Target binding supplies auth?"}
    E -- "Yes" --> U2["Dispatch with binding-owned target credential"]
    E -- "No" --> U3["Dispatch to intentionally keyless target without auth"]

    U1 --> R["Response translation and Relay lifecycle"]
    U2 --> R
    U3 --> R
Loading

This produces consistent outcomes across provider combinations:

Route outcome Credential owner Result
OpenAI source → same OpenAI route Source/normal Relay upstream policy Legitimate source auth remains usable.
OpenAI source → Anthropic target Target binding OpenAI auth is stripped; the Anthropic binding supplies its own auth.
Anthropic source → OpenAI target Target binding Anthropic key headers are stripped; the OpenAI binding supplies its own auth.
Any source → explicit keyless target Target binding, intentionally empty Neither the source key nor an ambient global key is forwarded.
Foreground gateway request Existing foreground policy No new invocation credential requirement or placeholder interpretation.

The important boundary is that plugins select a target but do not inherit authentication accidentally. Relay consumes its internal dispatch headers, strips source ownership, and applies only the credentials configured for the selected destination.

@bbednarski9
bbednarski9 marked this pull request as ready for review July 16, 2026 13:13
@bbednarski9
bbednarski9 requested a review from a team as a code owner July 16, 2026 13:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/cli/src/agents/claude/launch.rs`:
- Around line 24-31: Update the custom-header construction in the Claude launch
flow to remove all existing case-insensitive x-nemo-relay-proxy-token entries
before appending proxy_header, ensuring the newly appended token is the first
matching value. Add a regression test covering ANTHROPIC_CUSTOM_HEADERS that
already contains this header and verify only the current proxy token is used.

In `@crates/cli/tests/coverage/agents/launcher_tests.rs`:
- Around line 766-780: Create an EnvScope for ANTHROPIC_CUSTOM_HEADERS and clear
it before constructing the launch in this test, ensuring the scope remains
active through the assertions. Keep the existing custom-header and
secret-environment checks unchanged so the test verifies cross-request isolation
without inheriting process-wide state.

In `@crates/cli/tests/coverage/shared/gateway_tests.rs`:
- Around line 1128-1146: Update
transparent_proxy_rejects_missing_or_foreign_credentials to match each consume
failure against CliError::Unauthorized(_) rather than only asserting is_err(),
and convert the error through IntoResponse to assert StatusCode::UNAUTHORIZED.
Preserve the existing check that foreign credentials remain unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: ea940f23-fc7a-4e5d-b6e3-cecfd37944f3

📥 Commits

Reviewing files that changed from the base of the PR and between 51b6215 and 235ef2d.

📒 Files selected for processing (21)
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/error.rs
  • crates/cli/src/gateway/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/lib.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (9)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/cli/src/lib.rs
  • crates/cli/src/error.rs
  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/src/gateway/response.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/src/agents/shared/alignment.rs
  • crates/cli/src/gateway/routes.rs
  • crates/cli/src/process/prepared.rs
  • crates/cli/src/agents/codex/launch.rs
  • crates/cli/src/provider_auth.rs
  • crates/cli/src/agents/claude/launch.rs
  • crates/cli/src/agents/hermes/config.rs
  • crates/cli/src/agents/mod.rs
  • crates/cli/src/gateway/request.rs
  • crates/cli/src/process/launcher.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
  • crates/cli/src/server/mod.rs
  • crates/cli/src/gateway/mod.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/cli/tests/coverage/shared/mcp_tests.rs
  • crates/cli/tests/coverage/shared/installer_tests.rs
  • crates/cli/tests/coverage/agents/hermes_tests.rs
  • crates/cli/tests/coverage/shared/gateway_tests.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/cli/tests/coverage/agents/launcher_tests.rs
🔇 Additional comments (26)
crates/cli/tests/coverage/agents/hermes_tests.rs (1)

1161-1168: 📐 Maintainability & Code Quality

Run the required Rust validation and confirm error-path coverage.

These assertions correctly match the transparent Hermes configuration contract. Before handoff, run just test-rust, cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, and uv run pre-commit run --all-files. Also confirm that the suite covers invalid or missing model configuration paths, not only successful rewriting.

As per coding guidelines, Rust changes require the listed formatting, lint, test, and pre-commit checks. As per path instructions, tests should cover the changed API behavior, including error paths.

Sources: Coding guidelines, Path instructions

crates/cli/src/lib.rs (1)

22-22: 📐 Maintainability & Code Quality

Confirm the required Rust validation passes.

Please run cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, and uv run pre-commit run --all-files before handoff.

As per coding guidelines, “Any Rust change must run just test-rust,” cargo fmt --all, and cargo clippy --workspace --all-targets -- -D warnings.

Source: Coding guidelines

crates/cli/src/provider_auth.rs (1)

1-42: LGTM!

Also applies to: 44-86, 88-107, 109-168

crates/cli/src/error.rs (1)

37-38: LGTM!

Also applies to: 103-103

crates/cli/src/agents/hermes/config.rs (1)

67-73: LGTM!

crates/cli/tests/coverage/agents/launcher_tests.rs (1)

290-308: LGTM!

Also applies to: 783-816, 905-912, 1742-1745

crates/cli/tests/coverage/shared/mcp_tests.rs (1)

424-424: LGTM!

crates/cli/tests/coverage/shared/gateway_tests.rs (1)

94-106: LGTM!

Also applies to: 406-406, 424-545, 572-572, 607-607, 720-723, 914-917, 948-960, 1038-1126, 1374-1374, 1411-1411

crates/cli/src/process/launcher.rs (4)

148-155: LGTM!


321-336: LGTM!


406-433: LGTM!


547-554: LGTM!

crates/cli/src/agents/mod.rs (1)

386-397: LGTM!

crates/cli/src/agents/codex/launch.rs (1)

199-205: LGTM!

crates/cli/tests/coverage/shared/server_tests.rs (4)

397-397: LGTM!

Also applies to: 505-505, 573-573


430-493: LGTM!


2408-2492: LGTM!


3009-3011: LGTM!

crates/cli/tests/coverage/shared/installer_tests.rs (1)

60-67: 🎯 Functional Correctness

Confirm the credential reaches send_verified_hook_forward_request. If that helper does not inject the generated token/header, this test will 401 before exercising the transparent ingress path.

crates/cli/src/process/prepared.rs (1)

6-27: 📐 Maintainability & Code Quality

Run the Rust validation suite before handoff. cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, cargo deny check, and uv run pre-commit run --all-files are still required.

crates/cli/src/agents/shared/alignment.rs (1)

549-549: LGTM!

crates/cli/src/server/mod.rs (1)

60-61: LGTM!

Also applies to: 83-83, 104-110, 221-239, 257-288, 436-463, 480-511

crates/cli/src/gateway/routes.rs (1)

18-32: LGTM!

crates/cli/src/gateway/request.rs (1)

24-40: LGTM!

Also applies to: 55-69, 74-84

crates/cli/src/gateway/mod.rs (1)

66-72: LGTM!

Also applies to: 119-134, 243-255, 426-437, 688-728, 733-744, 763-846, 949-960, 1085-1105

crates/cli/src/gateway/response.rs (1)

59-71: LGTM!

Comment thread crates/cli/src/agents/claude/launch.rs
Comment thread crates/cli/tests/coverage/agents/launcher_tests.rs
Comment thread crates/cli/tests/coverage/shared/gateway_tests.rs
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
Signed-off-by: Bryan Bednarski <bbednarski@nvidia.com>
@willkill07

Copy link
Copy Markdown
Member

/ok to test 5f3780d

@bbednarski9

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit ad95440 into NVIDIA:main Jul 16, 2026
36 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jul 16, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug issue describes bug; PR fixes bug lang:rust PR changes/introduces Rust code size:L PR is large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: transparent agent runs lack invocation-bound gateway authentication

2 participants